package org.hackystat.timeline.service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.List;

import org.hackystat.sensorbase.resource.sensordata.jaxb.Property;
import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorData;
import org.jdom.output.XMLOutputter;
import org.restlet.Client;

import org.restlet.data.MediaType;
import org.restlet.data.Protocol;
import org.restlet.data.Request;
import org.restlet.resource.DomRepresentation;
import org.restlet.resource.Representation;
import org.restlet.resource.StringRepresentation;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
//import org.w3c.dom.Document;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.hackystat.timeline.resource.timelinedata.jaxb.Data;
import org.hackystat.timeline.resource.timelinedata.jaxb.Event;
import org.hackystat.timeline.resource.timelineproperties.jaxb.Properties;
import org.hackystat.timeline.resource.timelineproperties.jaxb.Properties.AnalysisType;

/**
 * Class that retrieves the files from SensorBase and the properties from the Property Resource,
 * constructs the XML ready to be mapped into the SimileTimeline.
 * 
 * @author Alexey R. Olkov
 * 
 */
public class FileDispatcher {

  /**
   * Request.
   */
  private Request request;

  private String hostName;

  /**
   * Constructor.
   * 
   * @param hostName host name
   * @param request a request
   */
  public FileDispatcher(String hostName, Request request) {
    this.request = request;
    this.hostName = hostName;
    System.out.println("File Dispatcher is active");
  }

  /**
   * Retrieves data from the SensorBase and constructs the XML file in Simile fprmat.
   * 
   * @return String
   */
  public Representation retrieveData() {
    Client client = new Client(Protocol.HTTP);
    String sensorBaseHost = "http://localhost:9876";
    String sensorBaseURI = "/sensorbase/sensordatatypes";
    String sensorBaseSchemaURI = "/sensorbase/xmlschema";
    String result = "";
    String color_passed = "#339900", color_failed = "#FF0000";

    // try {
    System.out.println("Trying to access Timeline Properties Resource");

    // ------ Now getting XMLSchema file.
    // RETRIEVING THE PROPERTIES (SET THE ADDRESS)
    Properties properties = null;
    System.out.println("Now retrieving the properties.");
    try {
      JAXBContext propContext = JAXBContext
          .newInstance("org.hackystat.sensorbase.resource.timelineproperties.jaxb");
      Unmarshaller propUnmarshaller = propContext.createUnmarshaller();
      String propPath = this.hostName + "/timelineservice/"
          + request.getAttributes().get("userkey") + "/properties/"
          + request.getAttributes().get("projectname");
      properties = (Properties) propUnmarshaller.unmarshal(client.get(propPath).getEntityAsDom()
          .getDocument());
    }
    catch (Exception e) {

    }
    System.out.println("Properties retrieved");

    // IT WILL BE ACCESSED FROM SENSORBASE LIKE THIS:
    // client.get(sensorBaseHost + sensorBaseSchemaURI + "/"
    // + request.getAttributes().get("sensordatatype")).getEntity();

    // THIS IS HOW IT WILL BE DONE and the representation will be returned from based on actual
    // sensor data.
    // DomRepresentation sensorData = client.get(sensorBaseHost + sensorBaseURI).getEntityAsDom();

    Data timelineData = new Data();
    List<Event> events = timelineData.getEvent();
    timelineData.setWikiSection("http://simile.mit.edu/shelf/");
    timelineData.setWikiUrl("SAMPLE");

    for (int i = 1; i <= 5; i++) {
      System.out.println("Trying to open the file:");
      System.out.println("xml\\" + request.getAttributes().get("projectname") + "\\"
          + request.getAttributes().get("userkey") + "\\sensordatatype\\"
          + request.getAttributes().get("sensordatatype") + "\\" + i + "-"
          + request.getAttributes().get("start_timestamp") + ".xml");

      try {
        JAXBContext context = JAXBContext
            .newInstance("org.hackystat.sensorbase.resource.sensordata.jaxb");
        Unmarshaller unmarshaller = context.createUnmarshaller();
        SensorData sensorData = (SensorData) unmarshaller.unmarshal(new File("xml\\"
            + request.getAttributes().get("projectname") + "\\"
            + request.getAttributes().get("userkey") + "\\sensordatatype\\"
            + request.getAttributes().get("sensordatatype") + "\\" + i + "-"
            + request.getAttributes().get("start_timestamp") + ".xml"));

        System.out.println("FILE PARSED");

        Event event = new Event();
        String starts, ends, tool, isDuration, isVisible = "true", color = "#FFFFFF", color_alt = "", dataType;

        DatatypeFactory df = DatatypeFactory.newInstance();
        Long timeStarts = Long.valueOf(sensorData.getTimestamp().toGregorianCalendar()
            .getTimeInMillis());
        starts = String.valueOf(new Date(timeStarts));
        event.setStart(starts);

        XMLGregorianCalendar calStarts = sensorData.getTimestamp();
        calStarts.add(df.newDuration(250000)); // duration of 250 seconds

        Long timeEnds = Long.valueOf(calStarts.toGregorianCalendar().getTimeInMillis());

        ends = String.valueOf(new Date(timeEnds));
        event.setEnd(ends);

        dataType = request.getAttributes().get("sensordatatype").toString();
        event.setTitle(dataType);
        boolean isDataTypeDefined = false;

        // Go through properties for all sensordatatypes trying to find a matching one
        for (AnalysisType type : properties.getAnalysisType()) {
          if (type.getName().equalsIgnoreCase(dataType)) {
            isDataTypeDefined = true;
            for (org.hackystat.timeline.resource.timelineproperties.jaxb.Property prop : type
                .getProperty()) {
              if (prop.getKey().equalsIgnoreCase("color")) {
                color = prop.getValue();
              }
              else if (prop.getKey().equalsIgnoreCase("color-alt")) {
                color_alt = prop.getValue();
              }
              else if (prop.getKey().equalsIgnoreCase("visible")) {
                isVisible = prop.getValue();
              }
            }
            break;
          }
        }
        // No properties are found for a given sensordatatype, locate the properties for "Unknown"
        if (!isDataTypeDefined) {
          for (AnalysisType type : properties.getAnalysisType()) {
            if (type.getName().equalsIgnoreCase("unknown")) {
              for (org.hackystat.timeline.resource.timelineproperties.jaxb.Property prop : type
                  .getProperty()) {
                if (prop.getKey().equalsIgnoreCase("color")) {
                  color = prop.getValue();
                }
                else if (prop.getKey().equalsIgnoreCase("visible")) {
                  isVisible = prop.getValue();
                }
              }
              break;
            }
          }
        }

        // Set visibility.

        event.setIsVisible(Boolean.valueOf(isVisible));

        // Go through the SensorData and read all properties
        for (Property property : sensorData.getProperties().getProperty()) {
          if (property.getKey().equalsIgnoreCase("result")) {
            System.out.println(property.getValue());
            if (property.getValue().equalsIgnoreCase("passed")) {
              event.setColor(color);
            }
            else {
              event.setColor(color_alt);
            }
            event.setColor(color);
          }
          else {
            event.setValue(event.getValue() + "&lt;BR&gt; &lt;B&gt;" + property.getKey()
                + ":&lt;/B&gt; " + property.getValue());
          }
        }

        // Set Duration (for now simply "true", but later will be calculated)
        event.setIsDuration(true);

        timelineData.getEvent().add(event);

      }
      catch (JAXBException e) {
        return null;
      }
      catch (DatatypeConfigurationException e) {
        return null;
      }

    }
    DocumentBuilder builder;
    try {
      builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
      Document doc = builder.newDocument();
      JAXBContext jaxbContext = JAXBContext
          .newInstance("org.hackystat.timeline.resource.timelinedata.jaxb");
      Marshaller marshaller = jaxbContext.createMarshaller();
      marshaller.marshal(timelineData, doc);
      return new DomRepresentation(MediaType.TEXT_XML, doc);
    }
    catch (Exception e) {
      return new StringRepresentation("PROBLEM:" + e.getLocalizedMessage());
    }

  }

  /**
   * Returns a request.
   * 
   * @return Request
   */
  Request getRequest() {
    return this.request;
  }
}
